/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_components/zhiqim_manager.htm
 *
 * Zhiqim Manager is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.manager.dao;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.zhiqim.httpd.HttpRequest;
import org.zhiqim.httpd.HttpSessionUser;
import org.zhiqim.kernel.annotation.AnAlias;
import org.zhiqim.kernel.json.Jsons;
import org.zhiqim.kernel.util.Asserts;
import org.zhiqim.kernel.util.Lists;
import org.zhiqim.kernel.util.Sqls;
import org.zhiqim.kernel.util.Validates;
import org.zhiqim.manager.ZmrBootstrap;
import org.zhiqim.manager.ZmrPassworder;
import org.zhiqim.manager.ZmrSessionUser;
import org.zhiqim.manager.dbo.ZmrDeptRule;
import org.zhiqim.manager.dbo.ZmrMenu;
import org.zhiqim.manager.dbo.ZmrOperator;
import org.zhiqim.manager.dbo.ZmrOperatorLog;
import org.zhiqim.manager.dbo.ZmrOperatorRule;
import org.zhiqim.manager.dbo.ZmrRoleRule;
import org.zhiqim.orm.ORM;
import org.zhiqim.orm.dbo.Selector;
import org.zhiqim.orm.dbo.Updater;

/**
 * 操作员数据访问对象
 *
 * @version v1.0.0 @author zouzhigang 2017-8-12 新建与整理
 */
@AnAlias("ZmrOperatorDao")
public class ZmrOperatorDao
{
    /*********************************************************************************************/
    //操作员基础信息
    /*********************************************************************************************/
    
    /**
     * 验证操作员密码
     * 
     * @param request       请求对象
     * @param operator      操作员对象
     * @param operatorPass  操作员密码
     * @return              =true表示成功
     * @throws Exception    可能的异常
     */
    public static boolean validateSecret(HttpRequest request, ZmrOperator operator, String operatorPass)
    {
        Asserts.as(operator!=null?null:"操作员对象不能为null");
        Asserts.as(!Validates.isEmptyBlank(operatorPass)?null:"操作员密码不能为空白");
        
        ZmrPassworder passworder = ZmrBootstrap.getPassworder();
        String encodePass = passworder.encode(operator.getOperatorCode(), operatorPass, operator.getOperatorPassSalt());
        return encodePass.equals(operator.getOperatorPass());
    }
    
    /**
     * 验证操作员密码
     * 
     * @param request       请求对象
     * @param operator      操作员对象
     * @param operatorPass  操作员密码
     * @return              =true表示成功
     * @throws Exception    可能的异常
     */
    public static boolean validatePassword(HttpRequest request, ZmrOperator operator, String operatorPass)
    {
        Asserts.as(operator!=null?null:"操作员对象不能为null");
        Asserts.as(!Validates.isEmptyBlank(operatorPass)?null:"操作员密码不能为空白");
        
        ZmrPassworder passworder = ZmrBootstrap.getPassworder();
        String encodePass = passworder.encode(operator.getOperatorCode(), operatorPass, operator.getOperatorPassSalt());
        return encodePass.equalsIgnoreCase(operator.getOperatorPass());
    }
    
    /**
     * 验证操作员密码
     * 
     * @param request       请求对象
     * @param operatorCode  操作员账号
     * @param operatorPass  操作员密码
     * @return              =true表示成功
     * @throws Exception    可能的异常
     */
    public static boolean validatePassword(HttpRequest request, String operatorCode, String operatorPass) throws Exception
    {
        Asserts.as(!Validates.isEmptyBlank(operatorCode)?null:"操作员账号不能为空白");
        Asserts.as(!Validates.isEmptyBlank( operatorPass)?null:"操作员密码不能为空白");
        
        ZmrOperator operator = ORM.table().item(ZmrOperator.class, operatorCode);
        if (operator == null)
            return false;
        
        return validatePassword(request, operator, operatorPass);
    }
    
    /**
     * 验证操作员是否有指定的菜单权限
     * 
     * @param request       请求对象
     * @param operatorCode  操作员账号
     * @param menuUrl       菜单URL
     * @return              =true表示有权限
     * @throws Exception    可能的异常
     */
    public static boolean hasMenuRule(HttpRequest request, String operatorCode, String menuUrl) throws Exception
    {
        Asserts.as(!Validates.isEmptyBlank(operatorCode)?null:"操作员账号不能为空白");
        Asserts.as(!Validates.isEmptyBlank(menuUrl)?null:"菜单URL不能为空白");
        
        List<ZmrMenu> menuList = getOperatorMenuList(request, operatorCode);
        for (ZmrMenu menu : menuList)
        {
            if (menuUrl.equals(menu.getMenuUrl()))
                return true;
        }
        
        return false;
    }
    
    /**
     * 验证操作员是否有指定的菜单权限
     * 
     * @param request       请求对象
     * @param operator      操作员对象
     * @param menuUrl       菜单URL
     * @return              =true表示有权限
     * @throws Exception    可能的异常
     */
    public static boolean hasMenuRule(HttpRequest request, ZmrOperator operator, String menuUrl) throws Exception
    {
        Asserts.as(operator != null?null:"操作员账号不能为空白");
        Asserts.as(!Validates.isEmptyBlank(menuUrl)?null:"菜单URL不能为空白");
        
        List<ZmrMenu> menuList = getOperatorMenuList(request, operator);
        for (ZmrMenu menu : menuList)
        {
            if (menuUrl.equals(menu.getMenuUrl()))
                return true;
        }
        
        return false;
    }
    
    /*********************************************************************************************/
    //操作员参数相关
    /*********************************************************************************************/
    
    /**
     * 获取操作员参数
     * 
     * @param request       请求对象
     * @param operatorCode  操作员账号
     * @param key           参数字段
     * @throws Exception    可能的异常
     */
    public static String getOperatorParam(HttpRequest request, String operatorCode, String key) throws Exception
    {
        Asserts.as(!Validates.isEmptyBlank(operatorCode)?null:"操作员账号不能为空白");
        
        ZmrOperator operator = ORM.table().item(ZmrOperator.class, operatorCode);
        if (operator == null)
            return null;
        
        return Jsons.getString(operator.getOperatorParam(), key);
    }
    
    /**
     * 获取操作员参数
     * 
     * @param request       请求对象
     * @param operator      操作员对象
     * @param key           参数字段
     * @throws Exception    可能的异常
     */
    public static String getOperatorParam(ZmrOperator operator, String key) throws Exception
    {
        Asserts.as(operator != null?null:"操作员账号不能为空白");
        
        return Jsons.getString(operator.getOperatorParam(), key);
    }
    
    /**
     * 更新操作员参数
     * 
     * @param request       请求对象
     * @param operatorCode  操作员账号
     * @param key           参数字段
     * @param value         参数值
     * @throws Exception    可能的异常
     */
    public static void addOrUpdateOperatorParam(HttpRequest request, String operatorCode, String key, Object value) throws Exception
    {
        ZmrOperator operator = ORM.table().item(ZmrOperator.class, operatorCode);
        if (operator == null)
            return;
        
        String operatorParam = operator.getOperatorParam();
        if (Validates.isEmptyBlank(operatorParam))
            operatorParam = "{}";
        operatorParam = Jsons.toStringAddOrUpdate(operatorParam, key, value);
        
        Updater updater = new Updater();
        updater.addField("operatorParam", operatorParam);
        updater.addMust("operatorCode", operatorCode);
        ORM.table().update(ZmrOperator.class, updater);
    }
    
    /**
     * 删除操作员参数
     * 
     * @param request       请求对象
     * @param operatorCode  操作员账号
     * @param key           参数字段
     * @throws Exception    可能的异常
     */
    public static void deleteOperatorParam(HttpRequest request, String operatorCode, String key) throws Exception
    {
        ZmrOperator operator = ORM.table().item(ZmrOperator.class, operatorCode);
        if (operator == null)
            return;
        
        String operatorParam = operator.getOperatorParam();
        if (Validates.isEmptyBlank(operatorParam))
            operatorParam = "{}";
        operatorParam = Jsons.toStringRemove(operatorParam, key);
        
        Updater updater = new Updater();
        updater.addField("operatorParam", operatorParam);
        updater.addMust("operatorCode", operatorCode);
        ORM.table().update(ZmrOperator.class, updater);
    }
    
    /*********************************************************************************************/
    //操作员权限相关
    /*********************************************************************************************/
    
    /**
     * 获取所有正常菜单列表
     * 
     * @param request       请求对象
     * @return              全部菜单列表
     * @throws Exception    可能的异常
     */
    public static List<ZmrMenu> getAllMenuList(HttpRequest request) throws Exception
    {
        Selector selector = new Selector();
        selector.addOrderbyAsc("menuLevel");
        selector.addOrderbyAsc("menuCode");
        
        return ORM.table().list(ZmrMenu.class, selector);
    }
    
    /**
     * 获取操作员菜单列表
     * 
     * @param request       请求对象
     * @param operatorCode  操作员账号
     * @return              菜单列表
     * @throws Exception    可能的异常
     */
    public static List<ZmrMenu> getOperatorMenuList(HttpRequest request, String operatorCode) throws Exception
    {
        Asserts.assertNotEmpty(operatorCode, "操作员账号不允许为空");
        
        ZmrOperator operator = ORM.table().item(ZmrOperator.class, operatorCode);
        if (operator == null)
            return new ArrayList<ZmrMenu>();
        
        return getOperatorMenuList(request, operator);
    }
    
    /**
     * 获取操作员菜单列表
     * 
     * @param request       请求对象
     * @param operator      操作员
     * @return              菜单列表
     * @throws Exception    可能的异常
     */
    public static List<ZmrMenu> getOperatorMenuList(HttpRequest request, ZmrOperator operator) throws Exception
    {
        List<ZmrMenu> menuList = getAllMenuList(request);
        if (operator.getOperatorType() == 0)
            return menuList;
        
        //1.删除未开启的功能菜单
        for (Iterator<ZmrMenu> it=menuList.iterator();it.hasNext();)
        {
            ZmrMenu menu = it.next();
            if (menu.getMenuStatus() != 0)
                it.remove();
        }
        
        //2.查出操作员独立权限、部门权限和角色权限
        List<ZmrOperatorRule> operatorRuleList = ORM.table().list(ZmrOperatorRule.class, new Selector("operatorCode", operator.getOperatorCode()));
        
        List<ZmrDeptRule> deptRuleList = new ArrayList<>();
        String deptIds = ZmrParamDao.isOperatorDeptAllRule()?operator.getOperatorDeptAll():operator.getOperatorDept();
        List<Long> deptIdList = Lists.toLongList(deptIds);
        for (long deptId : deptIdList)
        {
            List<ZmrDeptRule> ruleList = ORM.table().list(ZmrDeptRule.class, new Selector("deptId", deptId));
            deptRuleList.addAll(ruleList);
        }
        
        List<ZmrRoleRule> roleRuleList = new ArrayList<>();
        List<Long> roleIdList = Lists.toLongList(operator.getOperatorRole());
        for (long roleId : roleIdList)
        {
            List<ZmrRoleRule> ruleList = ORM.table().list(ZmrRoleRule.class, new Selector("roleId", roleId));
            roleRuleList.addAll(ruleList);
        }
        
        //3.属于三者之一则认为有效
        for (Iterator<ZmrMenu> it=menuList.iterator();it.hasNext();)
        {
            String menuCode = it.next().getMenuCode();
            if (isOperatorRule(operatorRuleList, menuCode))
                continue;
            
            if (isDeptRule(deptRuleList, menuCode))
                continue;
            
            if (isRoleRule(roleRuleList, menuCode))
                continue;

            it.remove();
        }
            
        return menuList;
    }
    
    /**
     * 查询所有操作员
     * 
     * @param request       请求对象
     * @return              操作员列表
     * @throws Exception    可能的异常
     */
    public static List<ZmrOperator> getOperatorAll(HttpRequest request) throws Exception
    {
        return ORM.table().list(ZmrOperator.class);
    }
    
    /**
     * 指定操作员账号查询操作员
     * 
     * @param request       请求对象
     * @param operatorCode  操作员编号
     * @return              操作员对象
     * @throws Exception    可能的异常
     */
    public static ZmrOperator getOperator(HttpRequest request, String operatorCode) throws Exception
    {
        return ORM.table().item(ZmrOperator.class, operatorCode);
    }
    
    /**
     * 增加操作日志，操作时间取Sqls.nowTimestamp()，操作描述为空
     * 
     * @param request        请求对象
     * @param operateFeature 操作功能
     * @throws Exception     可能的异常
     */
    public static void addOperateLog(HttpRequest request, String operateFeature) throws Exception
    {
        addOperateLog(request, operateFeature, Sqls.nowTimestamp(), null);
    }
    
    /**
     * 增加操作日志，操作时间取Sqls.nowTimestamp()
     * 
     * @param request        请求对象
     * @param operateFeature 操作功能
     * @param operateDesc    操作描述
     * @throws Exception     可能的异常
     */
    public static void addOperateLog(HttpRequest request, String operateFeature, String operateDesc) throws Exception
    {
        addOperateLog(request, operateFeature, Sqls.nowTimestamp(), operateDesc);
    }
    
    /**
     * 增加操作日志，四个参数都设置进来
     * 
     * @param request        请求对象
     * @param operateFeature 操作功能
     * @param operateTime    操作时间
     * @param operateDesc    操作描述
     * @throws Exception     可能的异常
     */
    public static void addOperateLog(HttpRequest request, String operateFeature, Timestamp operateTime, String operateDesc) throws Exception
    {
        ZmrSessionUser sessionUser = request.getSessionUser(ZmrSessionUser.class);
        if (sessionUser == null)
            return;
        
        ZmrOperatorLog log = new ZmrOperatorLog();
        log.setOperatorCode(sessionUser.getOperatorCode());
        log.setOperateTime(operateTime);
        log.setOperateIp(request.getRemoteAddr());
        log.setOperateFeature(operateFeature);
        log.setOperateDesc(operateDesc);
        
        ORM.table().insert(log);
    }
    
    /** 是否在操作员列表中 */
    public static boolean isOperator(List<ZmrOperator> list, String operatorCode)
    {
        for (ZmrOperator opr : list)
        {
            if (operatorCode.equals(opr.getOperatorCode()))
                return true;
        }
        
        return false;
    }
    
    /** 是否操作员独立权限 */
    public static boolean isOperatorRule(List<ZmrOperatorRule> ruleList, String menuCode)
    {
        for (ZmrOperatorRule rule : ruleList)
        {
            if (menuCode.equals(rule.getMenuCode()))
                return true;
        }
        
        return false;
    }
    
    /** 是否部门权限 */
    public static boolean isDeptRule(List<ZmrDeptRule> ruleList, String menuCode)
    {
        for (ZmrDeptRule rule : ruleList)
        {
            if (menuCode.equals(rule.getMenuCode()))
                return true;
        }
        
        return false;
    }
    
    /** 是否角色权限 */
    public static boolean isRoleRule(List<ZmrRoleRule> ruleList, String menuCode)
    {
        for (ZmrRoleRule rule : ruleList)
        {
            if (menuCode.equals(rule.getMenuCode()))
                return true;
        }
        
        return false;
    }
    
    /** 刷新所有在线用户会话 */
    public static void refresh(HttpRequest request) throws Exception
    {
        List<HttpSessionUser> sessionUserList = request.getContext().getSessionManager().getSessionUserList();
        for (HttpSessionUser sessionUser : sessionUserList)
        {
            if (!(sessionUser instanceof ZmrSessionUser))
                continue;
            
            ZmrSessionUser sUser = (ZmrSessionUser)sessionUser;
            List<ZmrMenu> menuList = ZmrOperatorDao.getOperatorMenuList(request, sUser.getOperator());
            sUser.setMenuList(request.getContext(), menuList);
        }
    }
}
